﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MacomberMapSystem.Common.Errors;
using MacomberMapSystem.Common.Serialization;
using MacomberMapSystem.Common.Database;
using System.Data.SQLite;
using System.Data.Common;
using MacomberMapSystem.Common.Internals;
using MacomberMapSystem.Common.CIM.Direct;

namespace MacomberMapSystem.Common.CIM
{
    /// <summary>
    /// This class provides a fake element that shows some key information to the system, but really isn't tied into CIM
    /// </summary>
    public class CIM_FakeElement : CIM_Element, IFormattable
    {
        #region Variable declarations
        private CIM_Element _Substation;

        /// <summary>The substation in which the component should seem to be</summary>      
        public override CIM_Element Substation
        {
            get {
                if (_Substation == null)
                    _Substation = base.Substation;
                return _Substation; }
        }

        private String _TEID;
        /// <summary>
        /// The TEID of the element
        /// </summary>
        public override String TEID
        {
            get { 
                
                //If we have no TEID known, try to find it.                
                if (_TEID == null)
                    if (!Attributes.TryGetValue("cim:identifiedObject.teid", out _TEID))
                        _TEID = (++CIM.MaxTEID).ToString();
                
                return _TEID; 
            
            }
        }

        /// <summary>Our collection of incoming links</summary>
        public List<int> _InLinks = new List<int>(10);


        /// <summary>Our collection of incoming links</summary>
        public override CIM_Link[] InLinks
        {
            get
            {
                CIM_DirectLink[] Out = new CIM_DirectLink[_InLinks.Count];
                for (int a = 0; a < _InLinks.Count; a++)
                    if (CIM is CIM_DirectModel)
                        Out[a] = new CIM_DirectLink((CIM as CIM_DirectModel).Elements[_InLinks[a]]);
                return Out;
            }
        }


        /// <summary>Our collection of outgoing links</summary>
        private CIM_RdfID[] OutLinkIDs;

        /// <summary>
        /// Report our list of attributes
        /// </summary>
        public override Dictionary<string, string> Attributes
        {
            get
            {
                return base.Attributes;
            }
        }

        /// <summary>
        /// Our collection of attributes
        /// </summary>
        public override MM_CIM_SortedCollection AttributeList
        {
            get
            {
                return _AttributeList;
            }
        }

        private MM_CIM_SortedCollection _AttributeList;

        /// <summary>
        /// Our collection of outgoing links
        /// </summary>
        public override CIM_Link[] OutLinks
        {
            get
            {
                List<CIM_Link> OutLinks = new List<CIM_Link>();
                foreach (CIM_RdfID rdfID in OutLinkIDs)
                {
                    CIM_Element Elem = CIM.FromRdfID(rdfID);
                    if (Elem != null)
                        OutLinks.Add(CIM_Link.FromElement(Elem));
                }
                return OutLinks.ToArray();
            }
            set
            {
                List<CIM_RdfID> OutIDs = new List<CIM_RdfID>();
                foreach (CIM_Link OutLink in value)
                    OutIDs.Add(OutLink.rdfID);
                OutLinkIDs = OutIDs.ToArray();
            }
        }

        #endregion

        /// <summary>
        /// Create a new fake element
        /// </summary>
        /// <param name="CIM"></param>
        /// <param name="rdfID"></param>
        /// <param name="Type"></param>
        /// <param name="Attributes"></param>
        /// <param name="OutLinks"></param>
        public CIM_FakeElement(String Type, CIM_RdfID rdfID, MM_CIM_SortedCollection Attributes, CIM_RdfID[] OutLinks, MM_Database_Model CIM)
            : base(CIM)
        {
            this.rdfID = rdfID;
            this.TypeName = Type;
            this.CIM = CIM;
            this._AttributeList = Attributes;
            this.InLinks = new CIM_Link[0];
            this.OutLinkIDs = OutLinks;
            this.Name = "?";
            foreach (KeyValuePair<String, string> kvp in Attributes)
                if (kvp.Key == "cim:IdentifiedObject.name")
                    this.Name = kvp.Value;
        }


        /// <summary>
        /// Create a new fake element
        /// </summary>
        /// <param name="CIM"></param>
        /// <param name="rdfID"></param>
        /// <param name="Type"></param>
        public CIM_FakeElement(String Type, CIM_RdfID rdfID, MM_Database_Model CIM)
            : base(CIM)
        {
            this.rdfID = rdfID;
            this.TypeName = Type;
            this.CIM = CIM;
            this.InLinks = new CIM_Link[0];
            this.OutLinks = new CIM_Link[0];
        }

        /// <summary>
        /// Create a fake element from a database
        /// </summary>
        /// <param name="dRd"></param>
        /// <param name="CIM"></param>
        public CIM_FakeElement(DbDataReader dRd, MM_Database_Model CIM)
            : base(dRd, CIM)
        {
            this.InLinks = new CIM_Link[0];
            this.OutLinks = new CIM_Link[0];
        }

        /// <summary>
        /// Create a new fake element
        /// </summary>
        /// <param name="Substation">The substation name</param>
        /// <param name="Name">The element name</param>
        /// <param name="Type">The element type</param>
        /// <param name="TEID">The TEID of the element</param>
        /// <param name="CIM">The CIM connector</param>
        /// <param name="Repository">The repository that may contain additionally needed elements</param>
        public CIM_FakeElement(String Substation, String Name, String Type, UInt64 TEID, MM_Database_Model CIM, MM_Repository Repository)
            : base(CIM)
        {
            this.InLinks = new CIM_Link[0];
            this.OutLinks = new CIM_Link[0];

            //First, assign our substation
            if (!String.IsNullOrEmpty(Substation))
                if (!CIM.Substations.TryGetValue(Substation, out _Substation))
                    _Substation = new CIM_FakeElement(null, Substation, "cim:Substation", ++CIM.MaxTEID, CIM, Repository);

            //Now, create our type
            int FoundType = CIM.Types.IndexOf(Type);
            if (FoundType == -1)
            {
                base.Type = (byte)CIM.Types.Count;
                CIM.Types.Add(Type);
            }
            else
                base.Type = (byte)FoundType;

            //Now, assign our TEID & Name
            this._TEID = TEID.ToString();
            this.Name = Name;
            this.rdfID = new CIM_RdfID("_" + Guid.NewGuid().ToString("D"));
            if (_Substation == null)
                MM_Event.LogMessage("Created fake {0} {1}", MM_Event.enumExceptionType.Warning, Type.Split(':')[1], Name);
            else
                MM_Event.LogMessage("Created fake {0} {1}.{2}", MM_Event.enumExceptionType.Warning, Type.Split(':')[1], _Substation.Name, Name);
        }
        #region Identity
        /// <summary>
        /// Report an easy-to-read string 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return base.ToString();
        }

        /// <summary>
        /// Present a formatted representation of the element
        /// </summary>
        /// <param name="format"></param>
        /// <param name="formatProvider"></param>
        /// <returns></returns>
        public new string ToString(string format, IFormatProvider formatProvider)
        {
            return this.ToString();
        }

        /// <summary>
        /// Return a hash code for this object
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.rdfID.GetHashCode();
        }
        #endregion
    }
}
